package com.mh.filter;


import com.mh.entity.LoginUser;
import com.mh.utils.JwtUtil;
import com.mh.utils.MyRedisCache;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//自定义认证过滤器,用来校验用户请求中携带的Token
@Component
public class MyJWtAuthFilter extends OncePerRequestFilter {
    //继承OncePerRequestFilter,为了简化过滤器编写+确保每个请求只被过滤一次

    private static final String REDIS_KEY_PREFIX = "login:";
    @Autowired
    private MyRedisCache redisCache;

    //重写抽象方法: 封装过滤器执行逻辑
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        //1.请求头中获取Token
        String token = request.getHeader("token");

        //2.判断Token是否为空,空直接放行
        //Spring提供的工具方法,用于检查字符串是否为空或仅包含空白字符
        //为空或仅包含空白字符,返回 false
        if (!StringUtils.hasText(token)) {
            //放行
            filterChain.doFilter(request, response);
            //return的目的: 返回响应时,避免走下面的逻辑,原路返回回去
            return;
        }

        //3.解析Token
        String uid = null;
        try {
            /** 解析传入的JWT字符串,返回Claims对象
             *  Claims: 这是JWT的负载部分,包含了JWT中的所有声明(Claims),例如用户ID、过期时间等
             *  parseJWT: 这是一个自定义的工具方法,通常基于JWT库(如 jjwt)实现,用于解析和验证JWT
             */
            Claims claims = JwtUtil.parseJWT(token);
            //JWT的标准声明之一,获取用户的唯一标识符
            uid = claims.getSubject();
        } catch (Exception e) {
            throw new RuntimeException("非法Token");
        }

        //4.从Redis中获取用户信息(通过key值获取全部用户信息)
        String redisKey = REDIS_KEY_PREFIX + uid;
        LoginUser loginUser = redisCache.getCacheObject(redisKey);
        //判断loginUser-->为空
        if (loginUser == null) {
            throw new RuntimeException("用户未登录");
        }

        //5.用户信息非空,将用户信息保存到SecurityContextHolder,方便后续的认证授权操作
        /** UsernamePasswordAuthenticationToken: 用于表示基于用户名和密码的认证信息(当前用户的认证信息)
         *  参数1_principal: 认证的主体,如UserDetails或自定义的LoginUser
         *  参数2_credentials: 凭证(如密码),认证成功后设置为null,以避免敏感信息泄露
         *  参数3_authorities: 用户的权限列表(GrantedAuthority集合),如果为 null,则表示用户没有任何权限
         *  参数3_authorities: 传入LoginUser类中编写的获取权限方法getAuthorities()返回值
         */
        Authentication upToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        /** 将认证信息（upToken）设置到当前线程的 SecurityContext 中
         *  getContext(): SecurityContextHolder(SCH)包含了当前用户的认证信息(认证对象)
         *  setAuthentication(upToken): 将上面Token设置为当前线程的认证信息-->后续可以在SCH中获取到认证信息
         */
        SecurityContextHolder.getContext().setAuthentication(upToken);

        //6.放行
        filterChain.doFilter(request, response);
    }
}
